package com.slofzx.nuoda.evaluation.version.builder;

import com.alibaba.fastjson.JSON;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.pagination.PaginationRenderPolicy;
import com.deepoove.poi.plugin.table.LoopColumnTableRenderPolicy;
import com.deepoove.poi.util.PoitlIOUtils;
import com.slofzx.nuoda.common.exception.ServiceException;
import com.slofzx.nuoda.common.util.DataConvertUtil;
import com.slofzx.nuoda.common.vo.CompanyVO;
import com.slofzx.nuoda.common.vo.FileVO;
import com.slofzx.nuoda.common.vo.SysUserVO;
import com.slofzx.nuoda.evaluation.compute.data.vo.ComputeDetailDataVO;
import com.slofzx.nuoda.evaluation.entity.ComputeData;
import com.slofzx.nuoda.evaluation.project.entity.PlanInfo;
import com.slofzx.nuoda.evaluation.project.entity.ProjectInfo;
import com.slofzx.nuoda.evaluation.project.enums.EvaluationMethodEnum;
import com.slofzx.nuoda.evaluation.project.enums.OilDisplacementMethodEnum;
import com.slofzx.nuoda.evaluation.project.util.BusiUtil;
import com.slofzx.nuoda.evaluation.project.util.DoubleUtil;
import com.slofzx.nuoda.evaluation.version.vo.ComputeVersionVO;
import com.slofzx.nuoda.evaluation.version.vo.ProjectReportVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ProjectReportBuilder {

    /**
     * 建设单位列表
     */
    private CompanyVO buildOffice;
    /**
     * 建设单位ID名称map
     */
    private Map<String, String> officeNameMap;

    private String userOfficeId;

    protected static final String FEASIBILITY_TEMPLATE = "/template/project_report.docx";
    private static final List<String> CONVENTIONAL_GROUP_KEY = Arrays.asList("JSTZZB", "DWCZCB_1", "SHNBSYL", "XMCWJXZ_SH_ZB", "XMTZHSQ_SH_ZB" ,"DWCZCB_MY_T","DWZCBFY","DWZCB_MY_T");
    private static final List<String> OPTIMIZE_INVEST_GROUP_KEY = Arrays.asList("JSTZZB", "SHNBSYL", "XMCWJXZ_SH_ZB", "XMTZHSQ_SH_ZB", "DWCZCB_1","DWCZCB_MY_T","DWZCBFY","DWZCB_MY_T"
            ,"ZCTZHJ","DMGCTZHJ","JSTZZB","LYTJ_PJJTZHJ","JSQLXZB","LDZJZB","ZTZZB","ZCTZBL","DMGCTZBL","JSTZBL","LYTJ_PJJTZBL","JSQLXBL","LDZJBL");
    private static final List<String> DEDUCT_APPORTIONMENT_GROUP_KEY = Arrays.asList("JSTZZB", "DWCZCB_1", "SHNBSYL", "XMCWJXZ_SH_ZB", "XMTZHSQ_SH_ZB","DWCZCB_MY_T","DWCZCBFY","DWZCBFY","DWZCB_MY_T");
    private static final List<String> SCALE_2_KEY = Arrays.asList( "NCY", "NCY_1", "NZS","DJRY","YJPJDJRCYL","investDownRate","finalInvestDownRate","SHNBSYL","conventional_SHNBSYL","optimize_invest_SHNBSYL","deduct_apportionment_SHNBSYL");
    private static final List<String> SCALE_1_KEY = Arrays.asList("SHTZHSQ","conventional_XMTZHSQ_SH_ZB","optimize_invest_XMTZHSQ_SH_ZB","deduct_apportionment_XMTZHSQ_SH_ZB","XMTZHSQ_SH_ZB");
    protected ProjectInfo projectInfo;
    /**
     * 项目基本信息
     */
    protected List<ProjectInfo> projectList;
    /**
     * 项目版本数据
     */
    private ComputeVersionVO computeVersion;

    private PlanInfo plan;

    private List<ComputeVersionVO> computeVersionList;

    private ComputeDetailDataVO computeDetailData;

    private List<ComputeDetailDataVO> computeDetailDataList;

    private String fileName;

    public ProjectReportBuilder fileName(String fileName) {
        this.fileName = fileName;
        return this;
    }

    public ProjectReportBuilder plan(PlanInfo plan) {
        this.plan = plan;
        return this;
    }

    public ProjectReportBuilder buildOffice(CompanyVO buildOffice) {
        this.buildOffice = buildOffice;
        return this;
    }

    public ProjectReportBuilder userOfficeId(String userOfficeId) {
        this.userOfficeId = userOfficeId;
        return this;
    }

    public ProjectReportBuilder officeNameMap (Map<String, String> officeNameMap) {
        this.officeNameMap = officeNameMap;
        return this;
    }

    // 利用利用 = 基建井数小计-钻井井数小计
    public ProjectReportBuilder project(ProjectInfo projectInfo) {
        this.projectInfo = projectInfo;
        return this;
    }

    public ProjectReportBuilder projectList(List<ProjectInfo> projectList) {
        this.projectList = projectList;
        return this;
    }

    public ProjectReportBuilder computeVersion(ComputeVersionVO computeVersionVO) {
        this.computeVersion = computeVersionVO;
        return this;
    }

    public ProjectReportBuilder computeVersionList(List<ComputeVersionVO> computeVersionList) {
        this.computeVersionList = computeVersionList;
        return this;
    }


    public ProjectReportBuilder computeDetailData(ComputeDetailDataVO computeDetailData) {
        this.computeDetailData = computeDetailData;
        return this;
    }

    public ProjectReportBuilder computeDetailDataList(List<ComputeDetailDataVO> computeDetailDataList) {
        this.computeDetailDataList = computeDetailDataList;
        return this;
    }

    /**
     * 构建下载文件
     *
     * @return
     */
    public FileVO build() {

        ProjectReportVO projectData = new ProjectReportVO();
        projectData.setName(plan.getName());
        if (MapUtils.isNotEmpty(officeNameMap)) {
            projectData.setBuildOffice(officeNameMap.get(projectData.getBuildOffice()));
            projectData.setUserBuildOffice(officeNameMap.get(userOfficeId));
        }
        projectData.setProjectYear(projectInfo.getYear());
        projectData.setInfOilWellCount(projectInfo.getInfOilWellCount());
        projectData.setInfWaterWellCount(projectInfo.getInfWaterWellCount());
        projectData.setBuiltUpCapacity(projectInfo.getBuiltUpCapacity());
        projectData.setDrillWellCount(projectInfo.getDrillWellCount());
        projectData.setDrillOilWellCount(projectInfo.getDrillOilWellCount());
        projectData.setDrillWaterWellCount(projectInfo.getDrillWaterWellCount());
        projectData.setDisplacementMethod(OilDisplacementMethodEnum.getEnum(projectInfo.getDisplacementMethod()).getName());
        projectData.setInfWellCount(projectInfo.getInfWellCount());
        projectData.setEvaluationPeriod(projectInfo.getEvaluationPeriod());
        projectData.setOldNetWorth(projectInfo.getOldNetWorth());
        projectData.setBuildYearCount(projectInfo.getBuildYearCount());
        projectData.setProduceYearCount(projectData.getEvaluationPeriod() - projectData.getBuildYearCount());
        // 设置评价方法名称
        EvaluationMethodEnum methodEnum = BusiUtil.getEnumById(projectInfo.getEvaluationMethod(), EvaluationMethodEnum.class);
        if (methodEnum != null) {
            String evaluationMethodName = StringUtils.EMPTY;
            switch (methodEnum){
                case CONVENTIONAL:
                    evaluationMethodName = "常规评价";
                    break;
                case OPTIMIZE_INVEST:
                    evaluationMethodName="优化投资评价";
                    break;
                case DEDUCT_APPORTIONMENT:
                    evaluationMethodName="扣除分摊费用评价";
                default:
                    break;

            }
            projectData.setEvaluationMethod(evaluationMethodName);
        }
        if (computeVersion.getIrrAfterTax() > 8D) {
            projectData.setFeasibility(true);
        } else {
            projectData.setNotFeasibility(true);
        }
        ComputeData computeData = null;
        if (computeDetailData != null && StringUtils.isNotEmpty(computeDetailData.getDetail())) {
            computeData = JSON.parseObject(computeDetailData.getDetail(), ComputeData.class);
        }
        Map<String, Object> groupDataMap = computeData.getGroupDataMap();
        if (computeData != null) {
            List<Map<String, Object>> yearDataList = new ArrayList<>();
            int cutoffYear = DataConvertUtil.parse2Int(groupDataMap.get("PJJZN"));
            if (cutoffYear - computeVersion.getCalculateYearStart() > 100) {
                throw new ServiceException("评价期大于100年，暂不支持导出", "评价期大于100年，暂不支持导出", computeVersion);
            }
            if (computeData.getResultDataMap() != null) {
                for (int year = computeVersion.getCalculateYearStart(); year <= cutoffYear; year++) {
                    Map<String, Object> rowData = new HashMap<>();
                    rowData.put("yearName", "第" + (year - computeVersion.getCalculateYearStart() + 1) + "年");
                    rowData.put("index", year - computeVersion.getCalculateYearStart() + 1);
                    rowData.put("year", year);
                    final Set<String> keySet = new HashSet<>(computeData.getResultDataMap().keySet());
                    for (String key : keySet) {
                        final Map<String, Map<Integer, Object>> resultDataMap = computeData.getResultDataMap();
                        Map<Integer, Object> yearDataMap = resultDataMap.get(key);
                        if (yearDataMap != null) {
                             Object value = yearDataMap.get(year);
                            if (SCALE_2_KEY.contains(key)) {
                                value = DoubleUtil.getScale(DataConvertUtil.parse2Double(value),2);
                            } else if (SCALE_1_KEY.contains(key)) {
                                value = DoubleUtil.getScale(DataConvertUtil.parse2Double(value),1);
                            }
                            rowData.put(key, value);
                        }
                    }

                    yearDataList.add(rowData);
                }
                //全部年份合计值
                Map<String, Object> sumColumnData = new HashMap<>();
                //第一年数据
                Map<String, Object> firstYearData = new HashMap<>();

                sumColumnData.put("yearName", "合计");
                for (Map.Entry<String, Map<Integer, Object>> entry : computeData.getResultDataMap().entrySet()) {
                    double sumResult = 0;
                    final String key = entry.getKey();
                    Map<Integer, Object> yearDataMap = computeData.getResultDataMap().get(key);
                    if (yearDataMap != null) {
                        List<String> percentageList = Arrays.asList("LDZJZJYCBBL", "LDZJJKBL", "LDZJJKLL", "GDZCDKLL", "YYSPL", "SDSL", "YYZZSL", "TRQZZSL", "ZYS", "CSWHJSS", "JYFFJ", "YYFL", "GDZCJKBL", "GDZCDKLL", "XYJZSYL");
                        for (int year = computeVersion.getCalculateYearStart(); year <= computeVersion.getCalculateYearEnd(); year++) {
                            double yearData = DataConvertUtil.parse2Double(yearDataMap.get(year));
                            if (percentageList.contains(key)) {
                                yearData = yearData * 100;
                            }
                            if (year == computeVersion.getCalculateYearStart()) {
                                firstYearData.put(key, yearData);
                                if (key.equals("YYSPL") || key.equals("LDZJJKLL")) {
                                    firstYearData.put(key, DoubleUtil.getScaleString(yearData, 2));
                                }
                                List<String> zsList = Arrays.asList("LDZJJKBL", "SDSL", "YYZZSL", "TRQZZSL", "ZYS", "CSWHJSS", "JYFFJ", "GDZCJKBL");
                                if (zsList.contains(key)) {
                                    firstYearData.put(key, DoubleUtil.getScaleString(yearData, 0));
                                }
                            }
                            sumResult += yearData;
                        }
                    }

                    sumColumnData.put(key, DoubleUtil.getScaleString(sumResult, 0));
                }
                // 处理合计含水率
//                processWaterRate(sumColumnData, hasWaterRate);
                projectData.setSumData(sumColumnData);
                projectData.setFirstYearData(firstYearData);
                projectData.setYearDataList(yearDataList);
            }
        }


        //导出数据
        String templatePath = FEASIBILITY_TEMPLATE;
        InputStream inputStream = ProjectReportBuilder.class.getResourceAsStream(templatePath);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // String fileName = plan.getName() + "可行性研究报告(经济评价).docx";
        FileVO result = new FileVO();
        result.setName(fileName);
        LoopColumnTableRenderPolicy columnPolicy = new LoopColumnTableRenderPolicy();
        PaginationRenderPolicy paginationRenderPolicy = new PaginationRenderPolicy();

        Configure configure = Configure.builder()
//                .useSpringEL()
//                .setRenderDataComputeFactory(model -> new CustomSpELRenderDataCompute(model, true))
                .bind("yearDataList", columnPolicy)
                .bind("pagination", paginationRenderPolicy)
//                .addPlugin('%', new PoiPicPolicy())
                .build();
        Map<String, Object> wordDataMap = new HashMap<>();
        try {
            final Map<String, ProjectInfo> projectInfoMap = projectList.stream().filter(f -> StringUtils.isNotEmpty(f.getEvaluationMethod())).collect(Collectors.toMap(ProjectInfo::getEvaluationMethod, Function.identity(), (v1, v2) -> v1));
            // 计算详情map
            final Map<String, ComputeDetailDataVO> detailDataVOMap = computeDetailDataList.stream().collect(Collectors.toMap(ComputeDetailDataVO::getId, Function.identity()));
            // 构建评价方法下生成的计算详情map
            Map<String, ComputeData> computeDataMap = new HashMap<>();
            for (Map.Entry<String, ProjectInfo> infoEntry : projectInfoMap.entrySet()) {
                final ProjectInfo entryValue = infoEntry.getValue();
                if (entryValue != null) {
                    final String computeId = entryValue.getComputeId();
                    if (StringUtils.isNotEmpty(computeId)) {
                        final ComputeDetailDataVO computeDetailDataVO = detailDataVOMap.get(computeId);
                        if (computeDetailDataVO != null) {
                            computeDataMap.put(infoEntry.getKey(), JSON.parseObject(computeDetailDataVO.getDetail(), ComputeData.class));
                        }

                    }

                }
            }
            // 分别设置 常规、投资优化、扣分摊三种方案的数据
            setGroupDataMapByMethod(computeDataMap, EvaluationMethodEnum.CONVENTIONAL, CONVENTIONAL_GROUP_KEY, groupDataMap);
            setGroupDataMapByMethod(computeDataMap, EvaluationMethodEnum.OPTIMIZE_INVEST, OPTIMIZE_INVEST_GROUP_KEY, groupDataMap);
            setGroupDataMapByMethod(computeDataMap, EvaluationMethodEnum.DEDUCT_APPORTIONMENT, DEDUCT_APPORTIONMENT_GROUP_KEY, groupDataMap);
            // 计算投资降低比例
            final String conventionalInvestKey = BusiUtil.generateBusiKey(EvaluationMethodEnum.CONVENTIONAL.getId(), "JSTZZB");
            final String optimizeInvestKey = BusiUtil.generateBusiKey(EvaluationMethodEnum.OPTIMIZE_INVEST.getId(), "JSTZZB");
            Double investDownRate = getInvestDownRate(conventionalInvestKey, optimizeInvestKey, groupDataMap);
            projectData.setInvestDownRate(investDownRate);
            // 计算结论投资降低比例
            Double finalInvestDownRate = getInvestDownRate(conventionalInvestKey, "JSTZZB", groupDataMap);
            projectData.setFinalInvestDownRate(finalInvestDownRate);

            groupDataMap = parseMapBigDecimal2String(groupDataMap, 0);
            Map<String, Object> projectDataMap = JSON.parseObject(JSON.toJSONString(projectData), HashMap.class);
            if (projectInfo.getInfWellCount() != null && projectInfo.getInfWellCount() != 0) {
                 Double straightWellOutput = projectInfo.getStraightWellOutput();
                double value = 0D;
                if (straightWellOutput == null) {
                    straightWellOutput = 0D;
                }
                final Integer infWellCount = projectInfo.getInfWellCount();
                if (infWellCount != null && infWellCount != 0) {
                     Integer buildHorizontalWellCount = projectInfo.getBuildHorizontalWellCount();
                    if (buildHorizontalWellCount == null) {
                        buildHorizontalWellCount = 0;
                    }
                     Double horizontalWellOutput = projectInfo.getHorizontalWellOutput();
                    if (horizontalWellOutput == null) {
                        horizontalWellOutput = 0D;
                    }
                    value = (straightWellOutput * (infWellCount - buildHorizontalWellCount) + horizontalWellOutput * buildHorizontalWellCount) / infWellCount;
                }
                projectDataMap.put("YJPJDJRCYL", value);
            }
            // 分别设置 常规、投资优化、扣分摊三种方案的评价期
            setEvaluationPeriod(projectDataMap, projectInfoMap, EvaluationMethodEnum.CONVENTIONAL);
            setEvaluationPeriod(projectDataMap, projectInfoMap, EvaluationMethodEnum.OPTIMIZE_INVEST);
            setEvaluationPeriod(projectDataMap, projectInfoMap, EvaluationMethodEnum.DEDUCT_APPORTIONMENT);
            projectDataMap = parseMapBigDecimal2String(projectDataMap, 0);
            wordDataMap.putAll(projectDataMap);
            wordDataMap.putAll(groupDataMap);
            wordDataMap.put("yearMonth", new SimpleDateFormat("yyyy年M月").format(new Date()));
            if (projectData.getDrillWellCount() != 0 && groupDataMap.get("ZTZZB") != null) {
                wordDataMap.put("DJTZ", Math.round(Double.parseDouble(groupDataMap.get("JSTZZB").toString()) / projectData.getDrillWellCount()));
            }
            if (projectData.getDrillWellCount() != 0 && groupDataMap.get("ZCTZHJ") != null) {
                wordDataMap.put("KFJDJTZ", Math.round(Double.parseDouble(groupDataMap.get("ZCTZHJ").toString()) / projectData.getDrillWellCount()));
            }
            if (projectData.getDrillWellCount() != 0 && groupDataMap.get("DMGCTZHJ") != null) {
                wordDataMap.put("DMGCDJTZ", Math.round(Double.parseDouble(groupDataMap.get("DMGCTZHJ").toString()) / projectData.getDrillWellCount()));
            }
            if (groupDataMap.get("JSTZZB") != null && projectData.getBuiltUpCapacity() != 0) {
                wordDataMap.put("BWDCN", Math.round(Double.parseDouble(groupDataMap.get("JSTZZB").toString()) / projectData.getBuiltUpCapacity() / 100));
            }
            XWPFTemplate.compile(inputStream, configure)
                    .render(wordDataMap).write(outputStream);
            result.setData(outputStream.toByteArray());
        } catch (IOException e) {
            throw new ServiceException("生成报告异常", "生成报告异常", e);
        } finally {
            PoitlIOUtils.closeLoggerQuietly(outputStream);
            PoitlIOUtils.closeLoggerQuietly(inputStream);
        }
        return result;
    }

    private Double getInvestDownRate(String key, String key2, Map<String, Object> groupDataMap) {
        final double conventionalInvest = DataConvertUtil.parse2Double(groupDataMap.get(key));
        if (conventionalInvest != 0) {
            Double investDownRate = (conventionalInvest -
                    DataConvertUtil.parse2Double(groupDataMap.get(key2))) / conventionalInvest * 100;
            // projectData.setInvestDownRate(investDownRate);
            return DoubleUtil.getScale(investDownRate, 2);
        }
        return 0D;
    }

    /**
     * 根据评价期设置不分年数据，支持多个值
     *
     * @param computeDataMap
     * @param methodEnum
     * @param keys
     * @param groupDataMap
     */
    private void setGroupDataMapByMethod(Map<String, ComputeData> computeDataMap, EvaluationMethodEnum methodEnum, List<String> keys, Map<String, Object> groupDataMap) {
        if (CollectionUtils.isEmpty(keys)) {
            return;
        }
        final ComputeData computeData = computeDataMap.get(methodEnum.getId());
        if (computeData != null) {
            final Map<String, Object> dataMap = computeData.getGroupDataMap();
            for (String key : keys) {
                groupDataMap.put(BusiUtil.generateBusiKey(methodEnum.getId(), key), dataMap.get(key));
            }
        }
    }

    /**
     * 根据评价方法设置评价期
     *
     * @param projectDataMap
     * @param projectInfoMap
     * @param methodEnum
     */
    private void setEvaluationPeriod(Map<String, Object> projectDataMap, Map<String, ProjectInfo> projectInfoMap, EvaluationMethodEnum methodEnum) {
        final ProjectInfo projectInfo = projectInfoMap.get(methodEnum.getId());
        if (projectInfo != null) {
            projectDataMap.put(BusiUtil.generateBusiKey(methodEnum.getId(), "evaluationPeriod"), projectInfo.getEvaluationPeriod());
        }
    }

    protected Map<String, Object> parseMapBigDecimal2String(Map<String, Object> dataMap, int digit) {
        List<String> oneDigitList = Arrays.asList("NCY", "NCY_1", "NZS", "DJRY");
        Map<String, Object> resultMap = new HashMap<>();
        if (dataMap == null) {
            return resultMap;
        }
        for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
            final String key = entry.getKey();
            final Object value = entry.getValue();
            if (value instanceof List) {
                List<Map<String, Object>> parseList = new ArrayList<>();
                List<Map<String, Object>> list = (List<Map<String, Object>>) value;
                for (Map<String, Object> item : list) {
                    parseList.add(parseMapBigDecimal2String(item, 0));
                }
                resultMap.put(key, parseList);
                continue;
            }
            if (value instanceof BigDecimal) {
                if (SCALE_2_KEY.contains(key)) {
                    resultMap.put(key, DoubleUtil.getScaleStringNew(((BigDecimal) value).doubleValue(), 2));
                } else if (SCALE_1_KEY.contains(key)) {
                    resultMap.put(key, DoubleUtil.getScaleStringNew(((BigDecimal) value).doubleValue(), 1));
                } else if (key.equals("builtUpCapacity")) {
                    resultMap.put(key, value);
                } else {
                    resultMap.put(key, DoubleUtil.getScaleString(((BigDecimal) value).doubleValue(), digit));
                }
            } else if (value instanceof Double) {
                if (SCALE_2_KEY.contains(key)) {
                    resultMap.put(key, DoubleUtil.getScaleStringNew((Double) value, 2));
                } else if (SCALE_1_KEY.contains(key)) {
                    resultMap.put(key, DoubleUtil.getScaleStringNew(((Double) value), 1));
                } else if (key.equals("builtUpCapacity")) {
                    resultMap.put(key, value);
                } else {
                    resultMap.put(key, DoubleUtil.getScaleString((Double) value, digit));
                }
            } else {
                resultMap.put(key, value);
            }
        }
        return resultMap;
    }

}
